<?php
/**
 * VerifyCodeService.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-02-18 00:22:13
 * @modified    2023-02-18 00:22:13
 *
 */

namespace App\Services;

use App\Library\Logger;
use App\Models\Customer\User;
use App\Repositories\JosemUserRepo;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;

class VerifyCodeService extends BaseService
{
    // 缓存 KEY 前缀
    const CODE_PREFIX = 'verify_code_';
    //
    const CODE_KEY = 'input_customer_user_account';

    // 过期时间
    const EXPIRED_MINUTE = 5;
    // 过期毫秒数
    const EXPIRED_TTL = self::EXPIRED_MINUTE * 60 * 1000;
    // 请求时间限制分钟数
    const REQUEST_LIMIT_SECOND = 1;

    private $account;
    private $logger;
    private $signature;

    public function __construct($account)
    {
        $this->account = $account;
        $this->logger = new Logger('verify_code');
        $this->signature = '嘉盛环境';
    }

    public function forResetPwd()
    {
        if (!JosemUserRepo::exist($this->account)) {
            throw new \Exception('手机号码不存在');
        }
        $this->generate();
    }

    /**
     * 生成验证码
     * 1、检查验证码是否存在于缓存中、是否已过期、如果存在且未过期则直接发送
     * 2、验证码请求间隔时间不低于一分钟，低于一分钟则抛异常
     * 3、验证码不存在或已过期：验证账号是否存在，账号存在则生成、存入缓存、发送
     * 4、验证码内容: 验证码 + 生成时间 + 联系人信息
     * @throws \Exception
     */
    public function generate()
    {
        $this->logger->info("生成验证码数据...");
        $account = $this->account;
        if (!verify_telephone($account) && !verify_email($account)) {
            throw new \Exception('账号错误, 请重试');
        }

        $verifyCodeKey = $this->generateKey();
        $verifyCodeCache = Cache::get($verifyCodeKey);
        // 一分钟内请求一次
        if (!empty($verifyCodeCache)) {
            $verifyCodeCreatedAt = $verifyCodeCache['created_at'];
            if (Carbon::parse($verifyCodeCreatedAt)->addMinutes(self::REQUEST_LIMIT_SECOND) > Carbon::now()->toDateTimeString()) {
                throw new \Exception('请求频率过高，请稍后再试');
            }
        }
        $customerUser = User::byPhone($account)->first();
        if (empty($customerUser)) {
            $this->logger->info("请求验证码账号 {$account} 无对应客户联系人");
        }
        $code = $this->generateCode();
        $createdAt = Carbon::now()->toDateTimeString();
        $verifyCodeData = [
            'code' => $code,
            'created_at' => $createdAt,
            'customer_user' => $customerUser
        ];
        $this->logger->info("验证码缓存数据如下: ");
        $this->logger->info($verifyCodeData);

        $this->send($code);
        Cache::put($verifyCodeKey, $verifyCodeData, self::EXPIRED_TTL);
    }

    /**
     * 检验验证码是否正确
     *
     * @param $code
     * @throws \Exception
     */
    public function validateCode($code)
    {
        if (!$code) {
            throw new \Exception('验证码不能为空');
        }
        $key = $this->generateKey();
        $verifyCodeCache = Cache::get($key);
        if (empty($verifyCodeCache)) {
            throw new \Exception('验证码不存在');
        }
        $cacheCode = $verifyCodeCache['code'];
        if ($cacheCode != $code) {
            throw new \Exception('验证码输入错误');
        }
        $createdAt = $verifyCodeCache['created_at'];
        $expiredAt = Carbon::parse($createdAt)->addMinutes(self::EXPIRED_MINUTE)->toDateTimeString();
        $now = Carbon::now()->toDateTimeString();
        if ($expiredAt <= $now) {
            $this->clear();
            throw new \Exception('验证码已过期');
        }
        $this->clear();
    }

    /**
     * 清空验证码
     */
    public function clear()
    {
        $key = $this->generateKey();
        Cache::forget($key);
    }

    /**
     * 发送验证码
     * @param string $code
     * @throws \Exception
     */
    public function send(string $code)
    {
        $account = $this->account;
        if (verify_telephone($account)) {
            $this->sendSms($account, $code);
        }
        if (verify_email($account)) {
            $this->sendEmail($account);
        }
    }

    /**
     * 发送短信验证码
     * @param string $account
     * @param string $code
     * @throws \Exception
     */
    private function sendSms(string $account, string $code)
    {
        (new SmsService($account))->sendVerifyCode($code);
    }

    /**
     * 发送邮件验证码
     * @param string $account
     * @param string $code
     */
    private function sendEmail(string $account, string $code)
    {

    }

    /**
     * 生成验证码code
     *
     * @return int
     */
    private function generateCode()
    {
        return rand(10000, 99999);
    }

    /**
     * 生成验证码缓存 KEY
     * @return string
     */
    private function generateKey()
    {
        return self::CODE_PREFIX . $this->account;
    }
}